import { isDef } from "@ct0/functions";
import { CaretDown as CaretDownIcon } from "@ct0/icons";
import Checkbox from "../../Checkbox";
import confirm from "../../confirm";
import message from "../../message";
import classNames from "classnames";
import React, { useLayoutEffect, useRef, useState } from "react";
import { isNew } from "./utils";
import { useStyles } from "./styles";
import type { TreeNodeProps } from "./type";

export { TreeNode, TreeNodeArrow };

function TreeNode({
  addChildText,
  currentKey,
  title,
  count,
  isLast,
  selected,
  checked,
  checkable,
  isLeaf,
  verticalLines,
  open,
  editable,
  isEditing,
  disableRemove,
  highlightSelected,
  disableAddChild,
  disableEdit,
  disableCheck,
  onEdit,
  nodeTitleChangeValidator,
  titleRender,
  onClick,
  onExpand,
  onTitleChange,
  onNodeRemove,
  onMockChildAdd,
  onCancelEdit,
  onCheck,
}: TreeNodeProps) {
  const classes = useStyles();

  const defaultValidator = (v: string) => (v ? undefined : "标题不能为空");
  const validator = nodeTitleChangeValidator ?? defaultValidator;

  const updateHandler = (v: string) => {
    const errmsg = validator?.(v);
    if (errmsg) {
      message.error(errmsg);
      return;
    }
    onTitleChange?.(v);
  };

  const remove = () => {
    confirm({ title: "提示", content: `确认删除 "${title}" 节点吗？` }).then(
      () => onNodeRemove?.()
    );
  };

  const add = () => {
    onExpand?.(true);
    onMockChildAdd?.();
  };

  const cancelHandler = () => {
    // 取消有两种情况，如果新增，那么直接删除当前节点，如果不是新增，则修改编辑状态
    if (isNew(currentKey)) onNodeRemove?.();
    onCancelEdit?.();
  };

  return (
    <div
      className={classNames(
        classes.node,
        selected && highlightSelected && classes.nodeActive
      )}
    >
      <TreeNodeIndent isLast={isLast} verticalLines={verticalLines} />
      {!isLeaf && <TreeNodeArrow open={open} onClick={onExpand} />}
      {checkable && (
        <Checkbox
          disabled={disableCheck}
          checked={checked}
          onChange={(_, checked) => onCheck?.(checked)}
        />
      )}
      {isEditing ? (
        <TreeNodeInput
          title={title}
          onCancel={cancelHandler}
          onConfirm={updateHandler}
        />
      ) : (
        <div className={classes.title} onClick={onClick}>
          {titleRender ?? title}
          {isDef(count) && <span> ({count})</span>}
        </div>
      )}
      {!isEditing && editable && (
        <>
          <div className={classes.growSpace}></div>
          {!disableEdit && (
            <div
              className={classNames(classes.hoverButton, "edit-btn")}
              onClick={onEdit}
            >
              编辑
            </div>
          )}
          {!disableRemove && (
            <div
              className={classNames(classes.hoverButton, "remove-btn")}
              onClick={remove}
            >
              删除
            </div>
          )}
          {!disableAddChild && (
            <div
              className={classNames(
                classes.hoverButton,
                "add-btn",
                open && classes.addChild
              )}
              onClick={add}
            >
              {addChildText}
            </div>
          )}
        </>
      )}
    </div>
  );
}

function TreeNodeInput({
  title,
  onCancel,
  onConfirm,
}: {
  title: string;
  onCancel: () => void;
  onConfirm: (v: string) => void;
}) {
  const [v, setV] = useState(title);
  const classes = useStyles();
  const dom = useRef<HTMLInputElement>(null);

  useLayoutEffect(() => {
    if (dom.current) {
      dom.current.focus();
      dom.current.scrollIntoView();
    }
  }, []);

  return (
    <>
      <input
        ref={dom}
        className={classes.input}
        value={v}
        onChange={(e) => setV(e.target.value)}
      />
      <div className={classes.growSpace}></div>
      <div className={classes.cancelButton} onClick={onCancel}>
        取消
      </div>
      <div className={classes.saveButton} onClick={() => onConfirm(v)}>
        保存
      </div>
    </>
  );
}

function TreeNodeIndent({
  verticalLines,
  isLast,
}: {
  verticalLines: Array<boolean>;
  isLast: boolean;
}) {
  const classes = useStyles();
  return (
    <>
      {verticalLines.map((b, index) => (
        <span className={classes.dashedLine} key={index}>
          {b && <div className={classes.dashedVer}></div>}
        </span>
      ))}
      <span className={classes.dashedLine}>
        <div
          className={classes.dashedVer}
          style={{ height: isLast ? "50%" : "100%" }}
        ></div>
        <div className={classes.dashedHor}></div>
      </span>
    </>
  );
}

function TreeNodeArrow({
  open,
  onClick,
}: {
  open?: boolean;
  onClick?: (open: boolean) => void;
}) {
  const classes = useStyles();
  return (
    <div className={classes.treeNodeArrow} onClick={() => onClick?.(!open)}>
      <div
        className={classes.treeNodeArrowIcon}
        style={{ transform: `rotate(${open ? 0 : -90}deg)` }}
      >
        <CaretDownIcon />
      </div>
      {open && <div className={classes.treeNodeArrowLine}></div>}
    </div>
  );
}
